package com.party.admin.biz.dataAnalyze;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.party.admin.web.dto.input.dataAnalyze.DataAnalyzeInput;
import com.party.admin.web.dto.output.dataAnalyze.CalcResult;
import com.party.admin.web.dto.output.dataAnalyze.DataAnalyzeOutput;
import com.party.common.utils.StringUtils;
import com.party.core.model.system.TargetType;
import com.party.core.service.article.IArticleService;
import com.party.core.service.circle.ICircleMemberService;
import com.party.core.service.collect.ISystemModuleCollectService;
import com.party.core.service.competition.ICompetitionProjectService;
import com.party.core.service.crowdfund.IProjectService;
import com.party.core.service.mall.IMallOrderDetailService;
import com.party.core.service.member.IMemberActService;
import com.party.core.service.photo.IPhotoProjectService;
import com.party.core.service.sign.ISignApplyService;
import com.party.core.service.sign.ISignProjectService;
import com.party.core.service.sign.ISignRecordService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.List;
import java.util.Map;

/**
 * @Description:
 * @Author: yangshoukun
 * @Date: 2018/6/27 10:26
 */
@Service
public class DataAnalyzeBizService {

    @Autowired
    private ISystemModuleCollectService systemModuleCollectService;

    @Autowired
    private IMemberActService memberActService;

    @Autowired
    private IProjectService projectService;

    @Autowired
    private ICircleMemberService circleMemberService;

    @Autowired
    private IMallOrderDetailService mallOrderDetailService;

    @Autowired
    private ISignProjectService signProjectService;

    @Autowired
    private ISignApplyService signApplyService;

    @Autowired
    private ISignRecordService signRecordService;

    @Autowired
    private ICompetitionProjectService competitionProjectService;

    @Autowired
    private IArticleService articleService;

    @Autowired
    private IPhotoProjectService photoProjectService;

    private final double ZERO = 0.0000000001;

    private final String NO_DATA = "-";

    /**
     * 查询
     * @param input
     * @return
     */
    public List<DataAnalyzeOutput> list(DataAnalyzeInput input) {

        if (StringUtils.isEmpty(input.getModuleCode()) || StringUtils.isEmpty(input.getDateRange1()) ||
                StringUtils.isEmpty(input.getDateRange2())) {
            return null;
        }

        List<DataAnalyzeOutput> result = Lists.newArrayList();

        String[] range1 = convertDate(input.getDateRange1());
        String[] range2 = convertDate(input.getDateRange2());

        // 浏览量与访客数
        Map<String, Object> pvAndUv1 = systemModuleCollectService.findByModuleAndDateRange(input.getModuleCode(), range1[0], range1[1]);
        Map<String, Object> pvAndUv2 = systemModuleCollectService.findByModuleAndDateRange(input.getModuleCode(), range2[0], range2[1]);

        // 浏览量
        DataAnalyzeOutput pv = getOutput("浏览量", "pvNum", pvAndUv1, pvAndUv2);
        result.add(pv);

        // 访客数
        DataAnalyzeOutput uv = getOutput("访客数", "uvNum", pvAndUv1, pvAndUv2);
        result.add(uv);

        Map<String, Object> params = Maps.newHashMap();
        params.put("partnerId", input.getPartnerId());
        if (TargetType.ACTIVITY.getCode().equals(input.getModuleCode())) {
            // 活动
            dealActivity(result, range1, range2, uv, params);
        } else if (TargetType.ZC_ACTIVITY.getCode().equals(input.getModuleCode())) {
            // 众筹
            if (null != input.getPartnerType()) {
                params.put("partnerType", input.getPartnerType());
            }
            dealCrowdfund(result, range1, range2, uv, params);
        } else if (TargetType.CIRCLE.getCode().equals(input.getModuleCode())) {
            // 圈子
            dealCircle(result, range1, range2, params);
        } else if (TargetType.MALL.getCode().equals(input.getModuleCode())) {
            // 商城
            dealMall(result, range1, range2);
        } else if (TargetType.SIGN.getCode().equals(input.getModuleCode())) {
            // 打卡
            dealSign(result, range1, range2, uv, params);
        } else if (TargetType.COMPETITION.getCode().equals(input.getModuleCode())) {
            // 赛事
            dealCompetition(result, range1, range2, params);
        } else if (TargetType.ARTICLE.getCode().equals(input.getModuleCode())) {
            // 文章
            dealArticle(result, range1, range2, uv, params);
        } else if (TargetType.PHOTO.getCode().equals(input.getModuleCode())) {
            // 相册
            dealPhoto(result, range1, range2, params);
        }

        return result;
    }

    /**
     * 活动
     * @param list
     */
    private void dealActivity(List<DataAnalyzeOutput> list, String[] range1, String[] range2, DataAnalyzeOutput uv, Map<String, Object> params) {
        Map<String, Object> m1 = memberActService.findDataByDate(range1[0], range1[1], params);
        Map<String, Object> m2 = memberActService.findDataByDate(range2[0], range2[1], params);

        // 报名人数
        DataAnalyzeOutput applyNum = getOutput("报名人数", "applyNum", m1, m2);
        list.add(applyNum);

        // 累计金额
        list.add(getOutput("累计金额", "money", m1, m2));

        // 报名转化率
        list.add(calcConversion("报名转化率", uv, applyNum, true));
    }

    /**
     * 众筹
     * @param list
     * @param range1
     * @param range2
     * @param uv
     */
    private void dealCrowdfund(List<DataAnalyzeOutput> list, String[] range1, String[] range2, DataAnalyzeOutput uv,
                               Map<String, Object> params) {
        Map<String, Object> m1 = projectService.countAnalyzeByDate(range1[0], range1[1], params);
        Map<String, Object> m2 = projectService.countAnalyzeByDate(range2[0], range2[1], params);

        // 报名人数
        DataAnalyzeOutput applyNum = getOutput("报名人数", "applyNum", m1, m2);
        list.add(applyNum);

        // 众筹金额
        DataAnalyzeOutput allMoney = getOutput("众筹金额", "allMoney", m1, m2);
        list.add(allMoney);

        // 众筹成功人数
        DataAnalyzeOutput successNum = getOutput("众筹成功人数", "successNum", m1, m2);
        list.add(successNum);

        // 众筹成功金额
        DataAnalyzeOutput successMoney = getOutput("众筹成功金额", "successMoney", m1, m2);
        list.add(successMoney);

        String[] tempIntroduction;

        // 报名转化率
        DataAnalyzeOutput applyRate = calcConversion("报名转化率", uv, applyNum, true);
        tempIntroduction = applyRate.getIntroductions();
        tempIntroduction[0] = "报名转化率=报名人数/访客数";
        list.add(applyRate);

        // 众筹人数成功率
        DataAnalyzeOutput numRate = calcConversion("众筹人数成功率", applyNum, successNum, true);
        tempIntroduction = numRate.getIntroductions();
        tempIntroduction[0] = "众筹人数成功率=众筹成功人数/报名人数";
        list.add(numRate);

        // 众筹金额成功率
        DataAnalyzeOutput moneyRate = calcConversion("众筹金额成功率", allMoney, successMoney, true);
        tempIntroduction = moneyRate.getIntroductions();
        tempIntroduction[0] = "众筹金额成功率=众筹成功金额/众筹金额";
        list.add(moneyRate);
    }

    /**
     * 圈子
     * @param list
     * @param range1
     * @param range2
     * @param params
     */
    private void dealCircle(List<DataAnalyzeOutput> list, String[] range1, String[] range2, Map<String, Object> params) {
        Map<String, Object> m1 = circleMemberService.countAnalyzeByDate(range1[0], range1[1], params);
        Map<String, Object> m2 = circleMemberService.countAnalyzeByDate(range2[0], range2[1], params);

        // 会员数
        list.add(getOutput("会员数", "memberNum", m1, m2));
    }

    /**
     * 商城
     * @param list
     * @param range1
     * @param range2
     */
    private void dealMall(List<DataAnalyzeOutput> list, String[] range1, String[] range2) {
        Map<String, Object> m1 = mallOrderDetailService.countAnalyzeByDate(range1[0], range1[1]);
        Map<String, Object> m2 = mallOrderDetailService.countAnalyzeByDate(range2[0], range2[1]);

        // 订单数
        DataAnalyzeOutput orderNum = getOutput("订单数", "orderNum", m1, m2);
        list.add(orderNum);

        // 交易金额
        DataAnalyzeOutput turnover = getOutput("交易金额", "turnover", m1, m2);
        list.add(turnover);

        // 下单人数
        DataAnalyzeOutput memberNum = getOutput("下单人数", "memberNum", m1, m2);
        list.add(memberNum);

        // 平均订单金额
        list.add(calcConversion("平均订单金额", orderNum, turnover, false));

        // 客单价
        list.add(calcConversion("客单价", memberNum, turnover, false));
    }

    /**
     * 打卡
     * @param list
     * @param range1
     * @param range2
     * @param uv
     * @param params
     */
    private void dealSign(List<DataAnalyzeOutput> list, String[] range1, String[] range2, DataAnalyzeOutput uv, Map<String, Object> params) {
        Map<String, Object> m1 = signProjectService.countAnalyzeByDate(range1[0], range1[1], params);
        Map<String ,Object> m2 = signProjectService.countAnalyzeByDate(range2[0], range2[1], params);

        // 项目数
        list.add(getOutput("项目数", "signProjectNum", m1, m2));

        // 报名人数
        m1 = signApplyService.countAnalyzeByDate(range1[0], range1[1], params);
        m2 = signApplyService.countAnalyzeByDate(range2[0], range2[1], params);
        DataAnalyzeOutput applyNum = getOutput("报名人数", "applayNum", m1, m2);
        list.add(applyNum);

        // 打卡人数
        m1 = signRecordService.countAnalyzeByDate(range1[0], range1[1], params);
        m2 = signRecordService.countAnalyzeByDate(range2[0], range2[1], params);
        DataAnalyzeOutput recordNum = getOutput("打卡人数", "recordNum", m1, m2);
        list.add(recordNum);

        // 报名转化率
        list.add(calcConversion("报名转化率", uv, applyNum, true));

        // 打卡率
        list.add(calcConversion("打卡率", applyNum, recordNum, true));
    }

    /**
     * 赛事
     * @param list
     * @param range1
     * @param range2
     * @param params
     */
    private void dealCompetition(List<DataAnalyzeOutput> list, String[] range1, String[] range2, Map<String, Object> params) {
        Map<String, Object> m1 = competitionProjectService.countAnalyzeByDate(range1[0], range1[1], params);
        Map<String, Object> m2 = competitionProjectService.countAnalyzeByDate(range2[0], range2[1], params);

        // 项目数
        list.add(getOutput("项目数", "competitionProjectNum", m1, m2));
    }

    /**
     * 文章
     * @param list
     * @param range1
     * @param range2
     * @param params
     */
    private void dealArticle(List<DataAnalyzeOutput> list, String[] range1, String[] range2, DataAnalyzeOutput uv, Map<String, Object> params) {
        Map<String, Object> m1 = articleService.countAnalyzeByDate(range1[0], range1[1], params);
        Map<String, Object> m2 = articleService.countAnalyzeByDate(range2[0], range2[1], params);

        // 文章数量
        DataAnalyzeOutput articleNum = getOutput("文章数量", "articleNum", m1, m2);
        list.add(articleNum);

        // 每篇平均浏览量
        list.add(calcConversion("每篇平均浏览量", articleNum, uv, false));
    }

    /**
     * 相册
     * @param list
     * @param range1
     * @param range2
     * @param params
     */
    private void dealPhoto(List<DataAnalyzeOutput> list, String[] range1, String[] range2, Map<String, Object> params) {
        Map<String, Object> m1 = photoProjectService.countAnalyzeByDate(range1[0], range1[1], params);
        Map<String, Object> m2 = photoProjectService.countAnalyzeByDate(range2[0], range2[1], params);

        // 项目数量
        DataAnalyzeOutput projectNum = getOutput("项目数量", "photoProjectNum", m1, m2);
        list.add(projectNum);

        // 操作次数
        DataAnalyzeOutput operateNum = getOutput("操作次数", "photoOperateNum", m1, m2);
        list.add(operateNum);
    }

    /**
     * 统计
     * @param name: 名字
     * @param key: map中对应的键
     * @param m1
     * @param m2
     * @return
     */
    private DataAnalyzeOutput getOutput(String name, String key, Map<String, Object> m1, Map<String, Object> m2) {
        String temp1 = judgeNull(key, m1);
        String temp2 = judgeNull(key, m2);
        CalcResult calcResult = calc(new BigDecimal(temp1), new BigDecimal(temp2));
        DataAnalyzeOutput output = new DataAnalyzeOutput();

        DecimalFormat format = new DecimalFormat("#.##");

        output.setName(name);
        output.setResult1(format.format(Double.parseDouble(temp1)));
        output.setResult2(format.format(Double.parseDouble(temp2)));
        output.setAddResult(calcResult.getAddResult());
        output.setAddRate(calcResult.getAddRate());
        return output;
    }

    private String judgeNull(String key, Map<String, Object> map) {
        return (null == map || null == map.get(key)) ? "0" : map.get(key).toString();
    }

    /**
     * 计算转化率
     * @param name
     * @param r1:分母
     * @param r2:分子
     * @return
     */
    private DataAnalyzeOutput calcConversion(String name, DataAnalyzeOutput r1, DataAnalyzeOutput r2, boolean hasPersent) {
        DataAnalyzeOutput output = new DataAnalyzeOutput();
        output.setName(name);

        DecimalFormat format;
        int multiple;
        String temp;
        if (hasPersent) {
            format = new DecimalFormat("0.00");
            multiple = 100;
            temp = "%";
        } else {
            format = new DecimalFormat("#.##");
            multiple = 1;
            temp = "";
        }

        // 除数/分母
        Double divisor1 = Double.valueOf(r1.getResult1());
        divisor1 = divisor1 - ZERO < 0 ? null : divisor1;
        // 被除数/分子
        Double dividend1 = Double.valueOf(r2.getResult1());
        Double conversion1 = null == divisor1 ? null : dividend1 * multiple / divisor1;

        Double divisor2 = Double.valueOf(r1.getResult2());
        divisor2 = divisor2 - ZERO < 0 ? null : divisor2;
        Double dividend2 = Double.valueOf(r2.getResult2());
        Double conversion2 = null == divisor2 ? null : dividend2 * multiple / divisor2;

        // 新增
        Double add = (null == conversion2 ? 0 : conversion2) - (null == conversion1 ? 0 : conversion1);

        // 新增比率
        Double addRate = null == conversion1 ? 0 : (add * 100 / (conversion1 - ZERO < 0 ? 1 : conversion1));

        output.setResult1(null == conversion1 ? NO_DATA : format.format(conversion1).concat(temp));
        output.setResult2(null == conversion2 ? NO_DATA : format.format(conversion2).concat(temp));
        output.setAddResult(null == conversion1 || null == conversion2 ? NO_DATA : format.format(add).concat(temp));
        output.setAddRate(null == conversion1 || null == conversion2 ? NO_DATA : format.format(addRate).concat("%"));

        return output;
    }

    private String[] convertDate(String dateRange) {
        final String split = "到";
        String[] strs = dateRange.split(split);
        for (int i = 0; i < strs.length; i++) {
            strs[i] = strs[i].trim();
        }
        strs[0] = strs[0].concat(" 00:00:00");
        strs[1] = strs[1].concat(" 23:59:59");
        return strs;
    }

    /**
     * 计算新增和新增百分比
     * @param param1: 减数
     * @param param2: 被减数
     * @return
     */
    private CalcResult calc(BigDecimal param1, BigDecimal param2) {
        param1 = param1 == null ? new BigDecimal(0) : param1;
        param2 = param2 == null ? new BigDecimal(0) : param2;

        CalcResult r = new CalcResult();
        Double re = param2.subtract(param1).doubleValue();

        r.setAddResult(new DecimalFormat("#.##").format(re));

        if ((param1.doubleValue() - ZERO) < 0) {
            // 分母为0
            r.setAddRate(NO_DATA);
        } else {
            String rate = new DecimalFormat("0.00").format(re * 100 / param1.doubleValue());
            rate = rate.concat("%");
            r.setAddRate(rate);
        }

        return r;
    }

    public List<Map<String, String>> getTypes() {
        List<Map<String, String>> list = Lists.newArrayList();

        list.add(typeToMap(TargetType.ACTIVITY));
        list.add(typeToMap(TargetType.ZC_ACTIVITY));
        list.add(typeToMap(TargetType.CIRCLE));
        list.add(typeToMap(TargetType.ARTICLE));
        list.add(typeToMap(TargetType.MALL));
        list.add(typeToMap(TargetType.SIGN));
        list.add(typeToMap(TargetType.COMPETITION));
        list.add(typeToMap(TargetType.PHOTO));

        return list;
    }

    private Map<String, String> typeToMap(TargetType type) {
        Map<String, String> map = Maps.newHashMap();
        map.put("code", type.getCode());
        if (TargetType.ACTIVITY == type) {
            map.put("value", "活动管理");
        } else if (TargetType.ZC_ACTIVITY == type) {
            map.put("value", "众筹管理");
        } else if (TargetType.CIRCLE == type) {
            map.put("value", "圈子管理");
        } else if (TargetType.MALL == type) {
            map.put("value", "商城管理");
        } else if (TargetType.SIGN == type) {
            map.put("value", "运动打卡");
        } else if (TargetType.COMPETITION == type) {
            map.put("value", "赛事成绩");
        } else if (TargetType.ARTICLE == type) {
            map.put("value", "文章管理");
        } else if (TargetType.PHOTO == type) {
            map.put("value", "行者相册");
        }
        return map;
    }
}
